//
//  main.swift
//  AutoXcodeProj
//  GitHub:https://github.com/tuist/xcodeproj
//  Created by jenkins on 2021/6/30.
//

import Foundation

//print("Hello, World!")
//✅ 💚 (绿心) 🎉 (庆祝) 🐛 (bug)  🆕 (全新) 💄 (口红) 🚀 (火箭) 🍎 (苹果) 🔥 🎨 ❗️❓‼️❌⁉️⚠️❎ ✨新增  🐛Bug
// Create a new flags object for the system-provided command-line arguments

var flags = Flags()
// Define the various flags
let filePath       = flags.strings("f", "filepath",description: ".xcodeproj文件路径（例：../Unity-iPhone.xcodeproj）")
let orginTagetName = flags.strings("o", "orginTagetName",description: "源Taget名称（例：Unity-iPhone）")
let toTagetName    = flags.strings("t", "toTagetName",description: "目标Taget名称（例：XXXFramework）")
let tagetList      = flags.option("l", "list",description: "工程所有Taget（使用示例：AutoXcodeProj -l）")
let inertCode      = flags.option("i", "insert",description: "移除PluginBase，插入相关代码（使用示例：AutoXcodeProj -i）")
let version      = flags.option("v", "version",description: "当前版本（使用示例：AutoXcodeProj -v）")
let help           = flags.option("h", "help",description: "显示此工具的使用说明和选项。\n示例🎉：AutoXcodeProj -f Unity-iPhone.xcodeproj -o Unity-iPhone -t NewFramework\n")

// Parse the command-line arguments and return error message if parsing fails
if let failure = flags.parsingFailure() {
    print(failure)
    exit(1)
}

func helpLogs() -> Void {
    print(flags.usageDescription(usageName: TextStyle.bold.properties.apply(to: "usage:"),
                                 synopsis: "[<option> ...] [---] [<program> <arg> ...]",
                                 usageStyle: TextProperties.none,
                                 optionsName: TextStyle.bold.properties.apply(to: "options:"),
                                 flagStyle: TextStyle.italic.properties),
          terminator: "")
}




// If help flag was provided, print usage description and exit tool
if help.wasSet {
    helpLogs()
  exit(0)
}

if version.wasSet {
    print("V2.0")
    exit(0)
}

/// 运行脚本
func runScript(xcodeprojPath:String) {
    let AutoXcodeProj_Plugin:String="/usr/local/bin/AutoXcodeProj_Plugin.py"
    let isProjectExist=FileManager.fileIsExists(path: AutoXcodeProj_Plugin as String)
    if !isProjectExist {
        print("‼️⚠️/usr/local/bin/AutoXcodeProj_Plugin.py插件不存在请检查!!\n：\(AutoXcodeProj_Plugin)")
        exit(0)
    }
    
    // Swift调用终端用Process类，这里先创建一个对象
    let pro = Process()
    // 指定编译器
    pro.launchPath = "/usr/local/bin/python3"
    
    // 设置要执行的语句，如果是有多条命令，用;隔开
    // 注意！因为命令是用""包在字符串里的，所以命令中有"时要用\"做转义
    pro.arguments = [AutoXcodeProj_Plugin,xcodeprojPath]
    
    let outputPipe = Pipe()
    pro.standardOutput = outputPipe
    var outFile = FileHandle()
    outFile = outputPipe.fileHandleForReading

    NotificationCenter.default.addObserver(forName: NSNotification.Name.NSFileHandleDataAvailable, object: outFile, queue: nil) { nti in
        let data = outFile.availableData
        let str = String(data: data, encoding: .utf8)
        if let str = str {
           print(str)
        }
    }
    outFile.waitForDataInBackgroundAndNotify()
    // 启动
    pro.launch()
    pro.waitUntilExit()
    print("替换代码完毕")
}


if inertCode.wasSet {
    let workPath:String! = FileManager.default.currentDirectoryPath
    let inputPath:String? = FileManager.getXcodeprojFilePath(workPath)
    if (inputPath == nil) {
        print("⚠️ ~.xcodeproj文件不存在，请检查终端工作目录是否为.xcodeproj文件所在目录!!\n")
        exit(0)
    }else{
        print(" ~.xcodeproj路径:\(inputPath!)")
    }
    let xcodePrjPath="\(workPath!)/\(inputPath!)"
    runScript(xcodeprojPath: xcodePrjPath)
}


if tagetList.wasSet {
    let workPath = FileManager.default.currentDirectoryPath
    let inputPath = FileManager.getXcodeprojFilePath(workPath)
    if (inputPath == nil) {
        print("⚠️ ~.xcodeproj文件不存在，请检查终端工作目录是否为.xcodeproj文件所在目录!!\n")
        exit(0)
    }
    let path = Path(inputPath!)
    let project = try XcodeProj(path:path)
    let pbxproj = project.pbxproj
    //所有taget
    print("🚀 工程Targets列表：")
    var dictM = [String : Any]()
    for (i, target) in pbxproj.nativeTargets.enumerated() {
        print("【\(i)】：\(target.name)")
        if target.productType == PBXProductType.application || target.productType == PBXProductType.framework {
            dictM[target.productType!.rawValue]=target.name
        }
    }
    print("\n------------建议命令-----------------")
    let xrfilePath = NSString(string:inputPath!)
    print("🚀 ~ AutoXcodeProj -f \(xrfilePath.lastPathComponent) -o \(dictM[PBXProductType.application.rawValue] ?? "") -t \(dictM[PBXProductType.framework.rawValue] ?? "")")
    exit(0)
}


if !filePath.wasSet ||  !orginTagetName.wasSet ||  !toTagetName.wasSet {
    print("Arguments:\(flags.arguments)")
    helpLogs()
    exit(0)
}

var inputPath = NSString(string: filePath.value.last!)
let workPath = NSString(string: FileManager.default.currentDirectoryPath)
//print("工作目录：\(workPath)")
if !inputPath.isAbsolutePath {
    inputPath=workPath.appendingPathComponent(inputPath as String) as NSString
}
let isProjectExist=FileManager.directoryIsExists(path: inputPath as String)
if !isProjectExist {
    print("‼️⚠️xcodeproj文件不存在请检查!!\n\(inputPath)")
    exit(0)
}

let projectPath = Path(inputPath as String)
// 1. Read the project
//let path = Path("/Users/jenkins/shdl/20210607zhillangchuanshuo/zlcs_21-06-25_1755/Unity-iPhone.xcodeproj")
let project = try XcodeProj(path: projectPath)
let pbxproj = project.pbxproj
let sourceTargetName = orginTagetName.value.last
let frameworkTargetName = toTagetName.value.last
let pbxProject = pbxproj.projects.first!
var unity_Target: PBXTarget!   //?相当于下面这种写法的语法糖
var buildPhasesHeaders: Set<PBXFileReference> = [] //头文件

pbxproj.fileReferences.forEach { fileRef in
    guard let fileName = fileRef.path else { return }
    if fileName.hasSuffix(".h") { // 所有头文件
        buildPhasesHeaders.insert(fileRef)
    }
}

//所有taget
pbxproj.nativeTargets.forEach { target in
    print("Target名称🍎：\(target.name) 类型：\(target.productType!)")
    if target.name == sourceTargetName {
        unity_Target=target;
    }
}

func startBuildSettingsConfigAction(target:PBXNativeTarget) -> Void {
    print("=======🚀Build Setting 配置中=========")
    let start = CFAbsoluteTimeGetCurrent()

    var unity_buildSettings = [String : Any]()
    var framework_buildSettings = [String : Any]()
    unity_Target.buildConfigurationList!.buildConfigurations.forEach { config in
        if(config.name=="Release"){
            unity_buildSettings=config.buildSettings
        }
    }
    
    target.buildConfigurationList!.buildConfigurations.forEach { config in
        //config.buildSettings=unity_TargetConfig.buildSettings //配置赋值
        framework_buildSettings=config.buildSettings
        //写入unity配置
        let ignore_keys0 = ["PRODUCT_NAME","INFOPLIST_FILE","LD_RUNPATH_SEARCH_PATHS","PRODUCT_BUNDLE_IDENTIFIER"]
        let ignore_keys2 = ["ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME","ASSETCATALOG_COMPILER_APPICON_NAME","PROVISIONING_PROFILE_SPECIFIER","INSTALL_PATH","DEBUG_INFORMATION_FORMAT"]
        let allIgnore_keys = ignore_keys0+ignore_keys2
        
        for (key, value) in unity_buildSettings {
            if framework_buildSettings.keys.contains(key){
//                print("unity_Target    ✅\(key)=\(value)")
//                print("framework_Target✅\(key)=\(framework_buildSettings[key] ?? "")")
                if allIgnore_keys.contains(key) {
//                    print("ignore_setting❌\(key)=\(value)")
                }else{
                    config.buildSettings[key]=value
                }
            }else{
//                print("framework_Target_no_contain❌\(key)=\(value)")
                if allIgnore_keys.contains(key) {
//                    print("ignore_setting2❌\(key)=\(value)")
                }else{
                    config.buildSettings[key]=value
                }
            }
        }

        //添加OTHER_LDFLAGS
        var otherldFlags:Array  = config.buildSettings["OTHER_LDFLAGS"] as! Array<String>
        otherldFlags.append("-undefined dynamic_lookup")
        config.buildSettings["OTHER_LDFLAGS"]=otherldFlags
        
        config.buildSettings["GCC_WARN_ABOUT_RETURN_TYPE"]="YES"
        config.buildSettings["GCC_C_LANGUAGE_STANDARD"] = "c99"
        config.buildSettings["CLANG_ENABLE_MODULES"]="NO"
        //是否支持测试:这个默认是NO。如果设置成YES，会导致两个文件中定义了相同的全局变量，编译时候会报错，重复定义变量。
        if(config.name=="Release"){
            config.buildSettings["ENABLE_TESTABILITY"]="NO"
            print("‼️⚠️ 默认添加-undefined dynamic_lookup标记，不需要请移除！（Build Settings > Other Linker Flags）")
        }
    }
    let end = CFAbsoluteTimeGetCurrent()
    let timespend=(end - start)*1000
    let timeStr = String(format:"%.2f",timespend)
    print("=======✅Build Setting 配置完成=========耗时:\(timeStr)毫秒")
//    for (key, value) in framework_buildSettings {
//        if unity_buildSettings.keys.contains(key){
//            print("    unity_Target2✅\(key)=\(value)")
//            print("    unity_Target2✅\(key)=\(unity_buildSettings[key] ?? "")")
//        }else{
//            print("unity_Target_no_contain❌\(key)=\(value)")
//        }
//    }
    
    //        print("\n\n=======✅Build Setting 配置比较开始=========")
    //        for (key, value) in unity_buildSettings {
    //            if framework_buildSettings.keys.contains(key){
    //                print("unity_Target    ✅\(key)=\(value)")
    //                print("framework_Target✅\(key)=\(framework_buildSettings[key] ?? "")")
    //            }else{
    //                print("framework_Target_no_contain❌\(key)=\(value)")
    //            }
    //        }
}


func startBuildPhasesConfigAction(target:PBXNativeTarget) -> Void {
    print("=======🚀BuildPhases 配置中=========")
    let start = CFAbsoluteTimeGetCurrent()

    let buildPhasesDict = NSMutableDictionary.init()
    //从unity_Target中获取buildPhases值
    for sourcePhase in unity_Target.buildPhases {
        if  sourcePhase.name() == "Frameworks"||sourcePhase.name() == "Headers"||sourcePhase.name() == "Sources" {
            buildPhasesDict[sourcePhase.name()!]=sourcePhase
        }else{
            //print("⚠️忽略UnknowName:\(sourcePhase.name()!)")
        }
    }
    
    for frameworkPhase in target.buildPhases {
        let phaseName=frameworkPhase.name()!
        print("开始处理buildPhases🎉:\(phaseName)")
        if phaseName == "Sources" {
            let sourcesPhase:PBXBuildPhase!=(buildPhasesDict["Sources"] as! PBXBuildPhase)
              sourcesPhase.files!.forEach { pbxFile in
                  let addFile = try! frameworkPhase.add(file: pbxFile.file!)
                  let setting=pbxFile.settings
                  if(setting?.isEmpty==false){
                      //添加COMPILER_FLAGS标记
                      addFile.settings=setting;
                  }
            }
            
            //移除main.mm
            frameworkPhase.files?.forEach({ pxbFile in
                if pxbFile.file?.name=="main.mm" {
                    frameworkPhase.files = frameworkPhase.files?.filter{$0 != pxbFile}
                }
            })
        }else if phaseName == "Frameworks" {
            let subframeworksPhase:PBXBuildPhase=buildPhasesDict["Frameworks"] as! PBXBuildPhase
            frameworkPhase.files=subframeworksPhase.files
        }else if phaseName == "Headers" {
            let publicFiles = ["UnityAppController.h","UnityController.h","RenderPluginDelegate.h","UnityInterface.h","UnityRendering","RegisterFeatures.h","UnityForwardDecls.h","LifeCycleListener.h"]
            buildPhasesHeaders.forEach { pbxFile in
                let addFile = try! frameworkPhase.add(file: pbxFile)
                let fileName = pbxFile.fileName() ?? ""
                if publicFiles.contains(fileName) {
                    addFile.settings=["ATTRIBUTES": ["Public"]]
                }
            }
        }
    }
    let end = CFAbsoluteTimeGetCurrent()
    let timespend:CGFloat=(end - start)*1000
    let timeStr = String(format:"%.2f",timespend)
    print("=======✅BuildPhases 配置完成=========耗时\(timeStr)毫秒")
}

//拷贝赋值-BuildPhases
pbxproj.nativeTargets.forEach { target in
//    target.buildConfigurationList!.buildConfigurations.forEach { config in
//        if(config.name=="Release"){
//            print("config.buildSettings✅\(target.name)=\(config.buildSettings)")
//        }
//    }
    
    if target.name == frameworkTargetName {
        //1、buildSettings设置
        startBuildSettingsConfigAction(target: target)
        
        //2、BuildPhases设置
        startBuildPhasesConfigAction(target: target)
        print("=======✅工程已配置成功！🎉🎉🎉=========")

    }
}

//保存proj
try project.write(path: projectPath)
